/*
* deadmethods - A unused methods detector
* Copyright 2011-2017 MeBigFatGuy.com
* Copyright 2011-2017 Dave Brosius
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and limitations
* under the License.
*/
package com.mebigfatguy.deadmethods;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Resource;
import org.apache.tools.ant.types.ResourceCollection;
import org.objectweb.asm.ClassReader;
public class ClassRepository implements Iterable<String> {
private final Path path;
private final ClassLoader loader;
private final Map<String, ClassInfo> classInfo;
public ClassRepository(Path classpath, Path auxClassPath) {
path = classpath;
loader = createClassLoader(classpath, auxClassPath);
classInfo = new HashMap<String, ClassInfo>();
}
public ClassInfo getClassInfo(String clsName) throws IOException {
if (clsName == null) {
return null;
}
ClassInfo info = classInfo.get(clsName);
if ((info == null) && !clsName.startsWith("[")) {
info = loadClassIntoRepository(clsName);
}
return info;
}
public Collection<ClassInfo> getAllClassInfos() {
return Collections.<ClassInfo>unmodifiableCollection(classInfo.values());
}
public Set<MethodInfo> getMethodInfo(String clsName) throws IOException {
ClassInfo info = classInfo.get(clsName);
if (info == null) {
info = loadClassIntoRepository(clsName);
}
return Collections.<MethodInfo>unmodifiableSet(info.getMethodInfo());
}
@Override
public Iterator<String> iterator() {
return new PathIterator(path, ".class");
}
public Iterator<String> xmlIterator() {
return new PathIterator(path, ".xml");
}
public Iterator<String> serviceIterator() {
return new PathPrefixIterator(path, "/META-INF/services");
}
private static final ClassLoader createClassLoader(final Path classpath, final Path auxClassPath) {
return AccessController.<URLClassLoader>doPrivileged(new PrivilegedAction<URLClassLoader>() {
@Override
public URLClassLoader run() {
Set<URL> urls = new HashSet<URL>();
urls.addAll(convertPathToURLs(classpath));
urls.addAll(convertPathToURLs(auxClassPath));
return new URLClassLoader(urls.toArray(new URL[urls.size()]));
}
});
}
private static List<URL> convertPathToURLs(ResourceCollection clsPath) {
List<URL> urls = new ArrayList<URL>();
Iterator<Resource> it = clsPath.iterator();
while (it.hasNext()) {
try {
Resource resource = it.next();
File file = new File(resource.toString());
if (file.exists()) {
if (file.getAbsolutePath().endsWith(".jar")) {
urls.add(new URL("jar", "", "file://" + file.getAbsolutePath() + "!/"));
} else {
urls.add(file.toURI().toURL());
}
} else {
TaskFactory.getTask().log("ClassPath root does not exist: " + file.getAbsolutePath());
}
} catch (MalformedURLException murle) {
//do something
}
}
return urls;
}
public InputStream getClassStream(String clsName) {
return loader.getResourceAsStream(clsName + ".class");
}
public InputStream getStream(String xmlName) {
return loader.getResourceAsStream(xmlName);
}
private ClassInfo loadClassIntoRepository(String clsName) throws IOException {
InputStream is = null;
try {
is = getClassStream(clsName);
ClassReader cr = new ClassReader(is);
ClassRepositoryVisitor crv = new ClassRepositoryVisitor();
cr.accept(crv, ClassReader.SKIP_DEBUG|ClassReader.SKIP_CODE);
ClassInfo info = crv.getClassInfo();
classInfo.put(clsName, info);
if (!"java/lang/Object".equals(clsName)) {
String superClassName = info.getSuperClassName();
ClassInfo superInfo = getClassInfo(superClassName);
if (superInfo != null) {
superInfo.addDerivedClass(info);
}
String[] interfaceNames = info.getInterfaceNames();
for (String interfaceName : interfaceNames) {
ClassInfo infInfo = getClassInfo(interfaceName);
infInfo.addDerivedClass(info);
}
}
return info;
} catch (Exception e) {
TaskFactory.getTask().log("Failed opening class into repository: " + clsName);
throw new IOException("Failed opening class into repository: " + clsName, e);
} finally {
Closer.close(is);
}
}
}